package io.noties.markwon.base

import io.noties.markwon.base.image.destination.ImageDestinationProcessor
import io.noties.markwon.base.image.load.AsyncDrawableLoader
import io.noties.markwon.base.image.size.ImageSizeResolver
import io.noties.markwon.base.image.size.ImageSizeResolverDef
import io.noties.markwon.base.order.DefaultPointIndex
import io.noties.markwon.base.order.IOrderIndex
import io.noties.markwon.base.syntax.SyntaxHighlight
import io.noties.markwon.base.syntax.SyntaxHighlightNoOp
import io.noties.markwon.plugin.callout.model.CallOutNode
import io.noties.markwon.prism4j.MyGrammarLocator
import io.noties.markwon.theme.MarkwonCategoryTheme
import io.noties.markwon.theme.MarkwonTheme
import io.noties.prism4j.Prism4j
import org.commonmark.node.Block
import org.commonmark.node.BlockQuote
import org.commonmark.node.FencedCodeBlock
import org.commonmark.node.Node

/**
 * since 3.0.0 renamed `SpannableConfiguration` -&gt; `MarkwonConfiguration`
 */
class MarkwonConfiguration private constructor(builder: Builder) {
    private val categoryTheme: MarkwonCategoryTheme
    private val asyncDrawableLoader: AsyncDrawableLoader
    private val syntaxHighlight: SyntaxHighlight
    private val linkResolver: LinkResolver

    // @since 4.4.0
    private val imageDestinationProcessor: ImageDestinationProcessor
    private val imageSizeResolver: ImageSizeResolver

    // @since 3.0.0
    private val spansFactory: MarkwonSpansFactory

    val fencedCodeBlockMap:Map<String, (FencedCodeBlock)->Block>
    val blockQuoteCallOutMap:Map<String, (BlockQuote,CallOutNode)->Block>

    val syntaxHighlightLocator: MyGrammarLocator

    val defaultOrderIndex:IOrderIndex

    val theme : MarkwonTheme
        get() = categoryTheme.currentTheme

    init {
        categoryTheme = builder.theme!!
        asyncDrawableLoader = builder.asyncDrawableLoader!!
        syntaxHighlight = builder.syntaxHighlight!!
        linkResolver = builder.linkResolver!!
        imageDestinationProcessor = builder.imageDestinationProcessor!!
        imageSizeResolver = builder.imageSizeResolver!!
        spansFactory = builder.spansFactory!!
        fencedCodeBlockMap = builder.fencedCodeBlockMap
        blockQuoteCallOutMap = builder.blockQuoteCallOutMap
        syntaxHighlightLocator = builder.syntaxHighlightLocator
        defaultOrderIndex = builder.defaultOrderIndex
    }

    fun categoryTheme(): MarkwonCategoryTheme {
        return categoryTheme
    }

    fun asyncDrawableLoader(): AsyncDrawableLoader {
        return asyncDrawableLoader
    }

    fun syntaxHighlight(): SyntaxHighlight {
        return syntaxHighlight
    }

    fun linkResolver(): LinkResolver {
        return linkResolver
    }

    /**
     * @since 4.4.0
     */
    fun imageDestinationProcessor(): ImageDestinationProcessor {
        return imageDestinationProcessor
    }

    fun imageSizeResolver(): ImageSizeResolver {
        return imageSizeResolver
    }

    /**
     * @since 3.0.0
     */
    fun spansFactory(): MarkwonSpansFactory {
        return spansFactory
    }

    fun <N : Node> spansFactory(node: Class<N>):SpanFactory?{
        return spansFactory[node]
    }

    class Builder internal constructor() {
        var theme: MarkwonCategoryTheme? = null
        var asyncDrawableLoader: AsyncDrawableLoader? = null
        var syntaxHighlight: SyntaxHighlight? = null
        var linkResolver: LinkResolver? = null

        // @since 4.4.0
        var imageDestinationProcessor: ImageDestinationProcessor? = null
        var imageSizeResolver: ImageSizeResolver? = null
        var spansFactory: MarkwonSpansFactory? = null

        var fencedCodeBlockMap = mutableMapOf<String, (FencedCodeBlock)->Block>()

        val blockQuoteCallOutMap = mutableMapOf<String, (BlockQuote,CallOutNode)->Block>()

        val syntaxHighlightLocator: MyGrammarLocator = MyGrammarLocator()

        val defaultOrderIndex: IOrderIndex = DefaultPointIndex()

        /**
         * @since 4.0.0
         */
        fun asyncDrawableLoader(asyncDrawableLoader: AsyncDrawableLoader): Builder {
            this.asyncDrawableLoader = asyncDrawableLoader
            return this
        }

        fun regeditFencedCodeBlock(lang:String,block:(FencedCodeBlock)->Block): Builder{
            fencedCodeBlockMap[lang] = block
            return this
        }

        fun regeditBlockQuoteCallOut(type:String,block:(BlockQuote,CallOutNode)->Block): Builder{
            blockQuoteCallOutMap[type]=block
            return this
        }

        fun syntaxHighlight(syntaxHighlight: SyntaxHighlight): Builder {
            this.syntaxHighlight = syntaxHighlight
            return this
        }

        fun linkResolver(linkResolver: LinkResolver): Builder {
            this.linkResolver = linkResolver
            return this
        }

        fun regeditSyntaxHighlightGrammar(name:String, block:(Prism4j) -> Prism4j.Grammar): Builder{
            syntaxHighlightLocator.regeditGrammar(name, block)
            return this
        }


        /**
         * @since 4.4.0
         */
        fun imageDestinationProcessor(imageDestinationProcessor: ImageDestinationProcessor): Builder {
            this.imageDestinationProcessor = imageDestinationProcessor
            return this
        }

        /**
         * @since 1.0.1
         */
        fun imageSizeResolver(imageSizeResolver: ImageSizeResolver): Builder {
            this.imageSizeResolver = imageSizeResolver
            return this
        }

        fun build(
            theme: MarkwonCategoryTheme,
            spansFactory: MarkwonSpansFactory
        ): MarkwonConfiguration {
            this.theme = theme
            this.spansFactory = spansFactory

            // @since 4.0.0
            if (asyncDrawableLoader == null) {
                asyncDrawableLoader = AsyncDrawableLoader.noOp()
            }
            if (syntaxHighlight == null) {
                syntaxHighlight = SyntaxHighlightNoOp()
            }
            if (linkResolver == null) {
                linkResolver = LinkResolverDef()
            }

            // @since 4.4.0
            if (imageDestinationProcessor == null) {
                imageDestinationProcessor = ImageDestinationProcessor.noOp()
            }
            if (imageSizeResolver == null) {
                imageSizeResolver = ImageSizeResolverDef()
            }
            return MarkwonConfiguration(this)
        }
    }

    companion object {
        fun builder(): Builder {
            return Builder()
        }
    }
}